#ifndef __ORDER_BUFFER_H__
#define __ORDER_BUFFER_H__

#include <stdlib.h>

#include <atomic>
#include <condition_variable>
#include <list>
#include <mutex>

// 为了简化处理逻辑，约定使用时必须分配CHUNK_SIZE的倍数(向上取整)，
// 这是c11里的aligned_alloc要求，另外linux的O_DIRECT也有规定
class Chunk
{
  public:
    Chunk();

    Chunk(const size_t off, const size_t sz, const uint8_t *src);

    Chunk(const size_t off, const size_t sz);

    Chunk(const Chunk &) = delete;

    Chunk &operator=(const Chunk &) = delete;

    Chunk(Chunk &&other);

    bool operator<(const Chunk &);

    ~Chunk();

    Chunk &operator=(Chunk &&other);

  public:
    // 非深拷贝更新(序列号禁止修改)
    void updateByNonDeepCopy(const size_t sz, uint8_t *&src);

    void clear();

  public:
    // 数据块的序列号(从0开始)
    size_t seq;
    // 实际大小(可能不是CHUNK_SIZE的倍数)
    size_t size;
    // data指向的堆内存必须用特定函数获取对齐内存，否则会出错，另一方面无法优化内存(cache 局部性)
    uint8_t *data;
};

// 管理Chunk(add, get, merge相邻), 针对hdd设计, 合并Chunk减少寻道、盘片旋转时间
class OrderBuffer
{
  public:
    OrderBuffer();

    OrderBuffer(const OrderBuffer &) = delete;

    OrderBuffer(OrderBuffer &&) = delete;

    ~OrderBuffer();

    OrderBuffer &operator=(const OrderBuffer &) = delete;

  public:
    // 缓冲区里增加一个Chunk
    void addChunk(Chunk &&chunk);

    // 必须按照"期望顺序"处理数据，否则后续写数据到磁盘会出错
    void getChunk(Chunk &);

    // 判断Buffer中是否为空(比如数据被消费完毕)
    bool isEmpty();

    void clear();

#ifdef __UNIT_TEST__
  public:
#else
  private:
#endif
    // 合并相邻的Chunk，为后续写到文件准备(不保证所有Chunk都能合并)
    bool mergeAdjacent();

  public:
    // 有序缓冲区，写入磁盘线程是逻辑层面的顺序写
    std::list<Chunk> buffer;

    // 消费线程期望的序列号
    std::atomic<size_t> expected{0};

    // 保护buffer, 约定对外接口加锁，内部函数不要使用锁，防止自己死锁
    std::mutex mtx;

    // 精准通知消费者取数据, 消除busy wait、减少上下文切换
    std::condition_variable consumerCond;
};

#ifdef __UNIT_TEST__
void testOrderBuffer();
#endif

#endif  //__ORDER_BUFFER_H__